#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include <stdlib.h>
#include <stdio.h>

#include <unistd.h>				//Needed for I2C port
#include <fcntl.h>				//Needed for I2C port
#include <sys/ioctl.h>			//Needed for I2C port
#include <linux/i2c-dev.h>		//Needed for I2C port
#include <unistd.h> 

#include <wiringPi.h>

#define DEVICE_ADDR     0x3C
#define DEVICE_CMD      0x80
#define DEVICE_DATA     0xC0

#define BL_GPIO         26      // WiringPi number

#define X_SIZE          256
#define Y_SIZE          128

static int  i2c_fd = -1;
static uint8_t  lcd_buffer[X_SIZE * Y_SIZE / 8 * 2] = {0xC0};

static void transfer_command_lcd(uint8_t cmd)
{
    uint8_t     buffer[2];
    buffer[0] = DEVICE_CMD;
    buffer[1] = cmd;
    write(i2c_fd, buffer, 2);
}

static void transfer_data_lcd(uint8_t data)
{
    uint8_t     buffer[2];
    buffer[0] = DEVICE_DATA;
    buffer[1] = data;
    write(i2c_fd, buffer, 2);
}

static void transfer_block_lcd()
{
    int ret;

    // LCD data address
	transfer_command_lcd(0x15);
	transfer_data_lcd(0);
	transfer_data_lcd(255);

	transfer_command_lcd(0x75);
	transfer_data_lcd(0);
	transfer_data_lcd(15);
	transfer_command_lcd(0x30);
	transfer_command_lcd(0x5c);

    // Send data
    ret = write(i2c_fd, lcd_buffer, sizeof(lcd_buffer));
    // printf("Write to i2c %d\r\n", ret);
}

static void Init()
{
    // // GPIO backlight PWM
    // if (wiringPiSetup () == -1)
    // {
    //     PyErr_SetFromErrno(PyExc_IOError);
    //     return;
    // }

    // // pinMode(BL_GPIO, PWM_OUTPUT);
    // pwmWrite(BL_GPIO, 1024);            // Turn on default

    i2c_fd = open("/dev/i2c-1", O_RDWR);
    if (i2c_fd == -1)
    {
        PyErr_SetFromErrno(PyExc_IOError);
        return;
    }

    if (ioctl(i2c_fd, I2C_SLAVE, DEVICE_ADDR) < 0)
    {
        close(i2c_fd);
        i2c_fd = -1;
        PyErr_SetFromErrno(PyExc_IOError);
        return;
    }

    transfer_command_lcd(0x30);
    transfer_command_lcd(0x94);
    transfer_command_lcd(0x31);
    transfer_command_lcd(0xD7);
    transfer_data_lcd(0X9F);

    transfer_command_lcd(0x32);
    transfer_data_lcd(0x00);
    transfer_data_lcd(0x01);
    transfer_data_lcd(0x04);

    transfer_command_lcd(0x20);
    transfer_data_lcd(0x01);
    transfer_data_lcd(0x03);
    transfer_data_lcd(0x05);
    transfer_data_lcd(0x07);
    transfer_data_lcd(0x09);
    transfer_data_lcd(0x0b);
    transfer_data_lcd(0x0d);
    transfer_data_lcd(0x10);
    transfer_data_lcd(0x11);
    transfer_data_lcd(0x13);
    transfer_data_lcd(0x15);
    transfer_data_lcd(0x17);

    transfer_data_lcd(0x19);
    transfer_data_lcd(0x1b);
    transfer_data_lcd(0x1d);
    transfer_data_lcd(0x1f);

    transfer_command_lcd(0x30);
    transfer_command_lcd(0x75);
    transfer_data_lcd(0X00);
    transfer_data_lcd(0X1f);
    transfer_command_lcd(0x15);
    transfer_data_lcd(0X00);
    transfer_data_lcd(0Xff);

    transfer_command_lcd(0xBC);
    transfer_data_lcd(0x00);

    transfer_command_lcd(0xA6);
    transfer_command_lcd(0x0c);

    transfer_command_lcd(0xCA);
    transfer_data_lcd(0X00);
    transfer_data_lcd(0X7F);
    transfer_data_lcd(0X20);

    transfer_command_lcd(0xF0);
    transfer_data_lcd(0X10);

    transfer_command_lcd(0x81);
    transfer_data_lcd(0x10);
    transfer_data_lcd(0x04);
    transfer_command_lcd(0x20);
    transfer_data_lcd(0x0B);

    for (int i = 0; i < sizeof(lcd_buffer) / 2; i++)
    {
        lcd_buffer[i * 2] = (uint8_t)DEVICE_DATA;
        lcd_buffer[i * 2 + 1] = 0;
    }

    transfer_command_lcd(0xAF);

    transfer_block_lcd();
}

static PyObject * Open(PyObject *self, PyObject *args)
{
    Init();
    Py_RETURN_NONE;
}

static PyObject * SetGlobalBright(PyObject *self, PyObject *args)
{
    Py_RETURN_NONE;
}

static PyObject * Display(PyObject *self, PyObject *args)
{
    PyObject   *object = NULL;
    Py_buffer   view;

    if (!PyArg_ParseTuple(args, "O", &object))
    {
        return NULL;
    }

    if (PyObject_GetBuffer(object, &view, PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT) == -1)
    {
        return NULL;
    }

    if (view.ndim != 1)
    {
        printf("Expected a 1-dimensional array\r\n");
        PyBuffer_Release(&view); 
        return NULL;
    }

        /* Check the type of items in the array */
    if (strcmp(view.format, "B") != 0) 
    { 
        printf("Expected an array of byte"); 
        PyBuffer_Release(&view); 
        return NULL; 
    }

    uint8_t*    data = (uint8_t*)view.buf;

    int row, col, i;
    uint8_t*    pData[8];
    uint8_t*    pBuffer[8];
    uint8_t     mask;

    for (col = 0; col < 32; col++)
    {
        for (row = 0; row < 128; row += 8)
        {
            pData[0] = data + (row * 32) + col;
            pData[1] = pData[0] + 32;
            pData[2] = pData[1] + 32;
            pData[3] = pData[2] + 32;
            pData[4] = pData[3] + 32;
            pData[5] = pData[4] + 32;
            pData[6] = pData[5] + 32;
            pData[7] = pData[6] + 32;

            pBuffer[0] = lcd_buffer + (row * 256 / 8 + col * 8) * 2 + 1;
            pBuffer[1] = pBuffer[0] + 2;
            pBuffer[2] = pBuffer[1] + 2;
            pBuffer[3] = pBuffer[2] + 2;
            pBuffer[4] = pBuffer[3] + 2;
            pBuffer[5] = pBuffer[4] + 2;
            pBuffer[6] = pBuffer[5] + 2;
            pBuffer[7] = pBuffer[6] + 2;

            mask = 0x80;
            for (i = 0; i < 8; i++)
            {
                *pBuffer[i] = ((*pData[0] & mask) ? 0x01 : 0x00) 
                            + ((*pData[1] & mask) ? 0x02 : 0x00)
                            + ((*pData[2] & mask) ? 0x04 : 0x00)
                            + ((*pData[3] & mask) ? 0x08 : 0x00)
                            + ((*pData[4] & mask) ? 0x10 : 0x00)
                            + ((*pData[5] & mask) ? 0x20 : 0x00)
                            + ((*pData[6] & mask) ? 0x40 : 0x00)
                            + ((*pData[7] & mask) ? 0x80 : 0x00);

                mask >>= 1;
            }
        }
    }

	// 数据转换到lcd_buffer后，view对象就没有用处了，可以释放（这个变量使用和LED那个还不同）
	PyBuffer_Release(&view);

    transfer_block_lcd();

    Py_RETURN_NONE;
}

static PyMethodDef lcd_driverMethods[] = {
    {"Display",  Display, METH_VARARGS, "Send data to LCD controller."},
    {"Open", Open, METH_VARARGS, "Open I2C1 PORT."},
    {"SetGlobalBright", SetGlobalBright, METH_VARARGS, "Set global bright."},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

static struct PyModuleDef lcd_drivermodule = {
    PyModuleDef_HEAD_INIT,
    "lcd_driver",   /* name of module */
    NULL, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                 or -1 if the module keeps state in global variables. */
    lcd_driverMethods
};

PyMODINIT_FUNC
PyInit_lcd_driver(void)
{
    return PyModule_Create(&lcd_drivermodule);
}

